#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__='Jack Zhang'

'''
async web application.
'''

import logging

logging.basicConfig(level=logging.INFO)

import asyncio,os,json,time
from datetime import datetime

from aiohttp import web
from jinja2 import Environment,FileSystemLoader

from config import configs

import orm
from coroweb import addRoutes,addStatic

from handlers import cookie2user,COOKIE_NAME

def init_jinja2(app,**kw):
    logging.info('init jinja2...')
    options=dict(
        autoescape=kw.get('autoescape',True),
        block_start_string=kw.get('block_start_string','{%'),
        block_end_string=kw.get('block_end_string','%}'),
        variable_start_string=kw.get('variable_start_string','{{'),
        variable_end_string=kw.get('variable_end_string','}}'),
        auto_reload=kw.get('auto_reload',True)
    )
    path=kw.get('path',None)
    if path is None:
        path=os.path.join(os.path.dirname(os.path.abspath(__file__)),'templates')
    logging.info('set jinja2 template path: %s'%path)
    env=Environment(loader=FileSystemLoader(path),**options)
    filters=kw.get('filters',None)
    if filters is not None:
        for name,filter in filters.items():
            env.filters[name]=filter
    app['__templating__']=env


async def loggerFactory(app,handler):
    async def logger(request):
        logging.info('Request: %s %s'%(request.method,request.path))
        # await asyncio.sleep(0.3)
        return (await handler(request))

    return logger

async def dataFactory(app,handler):
    async def parse_data(request):
        if request.method=='POST':
            if request.content_type.startswith('application/json'):
                request.__data__=await request.json()
                logging.info('request json: %s'%str(request.__data__))
            elif request.content_type.startswith('application/x-www-form-urlencoded'):
                request.__data__=await request.post()
                logging.info('request form: %s'%str(request.__data__))
        return (await handler(request))

    return parse_data

async def responseFactory(app,handler):
    async def response(request):
        logging.info('Response handler...')
        r=await handler(request)
        if isinstance(r,web.StreamResponse):
            return r
        if isinstance(r,bytes):
            resp=web.Response(body=r)
            resp.content_type='application/octet-stream'
            return resp
        if isinstance(r,str):
            if r.startswith('redirect:'):
                return web.HTTPFound(r[9:])
            resp=web.Response(body=r.encode('utf-8'))
            resp.content_type='text/html;charset=utf-8'
            return resp
        if isinstance(r,dict):
            template=r.get('__template__')
            if template is None:
                resp=web.Response(body=json.dumps(r,ensure_ascii=False,default=lambda o:o.__dict__).encode('utf-8'))
                resp.content_type='application/json;charset=utf-8'
                return resp
            else:
                resp=web.Response(body=app['__templating__'].get_template(template).render(**r).encode('utf-8'))
                resp.content_type='text/html;charset=utf-8'
                return resp
        if isinstance(r,int) and r>=100 and r<600:
            return web.Response(r)
        if isinstance(r,tuple) and len(r)==2:
            t,m=r
            if isinstance(t,int) and t>=100 and t<600:
                return web.Response(t,str(m))
        # default:
        resp=web.Response(body=str(r).encode('utf-8'))
        resp.content_type='text/plain;charset=utf-8'
        return resp

    return response

async def authFactory(app,handler):
    async def auth(request):
        logging.info('check user: %s %s'%(request.method,request.path))
        request.__user__=None
        cookieStr=request.cookies.get(COOKIE_NAME)
        if cookieStr:
            user=await cookie2user(cookieStr)
            if user:
                logging.info('set current user: %s'%user.email)
                request.__user__=user
        if request.path.startswith('/manage/') and (request.__user__ is None or not request.__user__.admin):
            return web.HTTPFound('/signin')
        return (await handler(request))

    return auth

def datetimeFilter(t):
    delta=int(time.time()-t)
    if delta<60:
        return u'1分钟前'
    if delta<3600:
        return u'%s分钟前'%(delta//60)
    if delta<86400:
        return u'%s小时前'%(delta//3600)
    if delta<604800:
        return u'%s天前'%(delta//86400)
    dt=datetime.fromtimestamp(t)
    return u'%s年%s月%s日'%(dt.year,dt.month,dt.day)

async def init(loop):
    await orm.createPool(loop=loop,**configs.db)
    app=web.Application(loop=loop,middlewares=[
        loggerFactory,responseFactory,authFactory
    ])
    init_jinja2(app,filters=dict(datetime=datetimeFilter))
    addRoutes(app,'handlers')
    addStatic(app)
    appRunner=web.AppRunner(app=app)
    await appRunner.setup()
    server=await loop.create_server(appRunner.server,'127.0.0.1',9000)
    logging.info('server started at http://127.0.0.1:9000...')
    return server

if __name__=='__main__':
    loop=asyncio.get_event_loop()
    loop.run_until_complete(init(loop))
    loop.run_forever()